/*
 *	Copyright (C) 2008 CRIMERE
 *	Copyright (C) 2008 Jean-Marc Mercier
 *	
 *	This file is part of OTS (optimally transported schemes), an open-source library
 *	dedicated to scientific computing. http://code.google.com/p/optimally-transported-schemes/
 *
 *	CRIMERE makes no representations about the suitability of this
 *	software for any purpose. It is provided "as is" without express or
 *	implied warranty.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#if !defined _analyzer_performance_
#define _analyzer_performance_
#include <map>
#include <set>
#include <string>
#include <time.h>
#include <src/miscellaneous/OS_basic.h>
#include <iostream>

template<class Analyzer_Cell >
struct Call_Graph {
	typedef Analyzer_Cell								analyzer_cell_type;
	typedef typename analyzer_cell_type::cell_type		cell_type;
	typedef typename cell_type::cell_parameter_type		cell_parameter_type;
	typedef Call_Graph*									ptr_type;
	typedef smart_ptr<Call_Graph>						smart_ptr_type;
	typedef std::map<std::string,smart_ptr_type>		map_type;

	Call_Graph() : Parent_(0) {};
	Call_Graph(Call_Graph* Parent) : Parent_(Parent){
	};
	virtual ~Call_Graph(){
	};
	void operator()(const cell_parameter_type & arg) {
		Cell_(arg);
	};

	Call_Graph*				Parent_;
	map_type				Childs_;
	cell_type				Cell_;
};

template<class T>
std::ostream &operator<<(std::ostream &out, const Call_Graph<T>& v) {
		typedef Call_Graph<T> graph_type;
		out << std::endl << " stat for : " << v.Cell_ << std::endl;
		graph_type::map_type::const_iterator it = v.Childs_.begin(), it_end = v.Childs_.end();
		if (v.Parent_ != 0 || v.Childs_.size()>0) {
			for (;it != it_end;++it) {
				OS_double percent = 100.;
				out << "--------------"<< it->second->Cell_<<std::endl;
				if (v.Cell_() > 0) {
					percent = 100.*it->second->Cell_() / v.Cell_();
					out << "  (" <<  percent << " % )";
				};
			}
		}
		it = v.Childs_.begin();
		for (;it != it_end;++it)
				out << *(it->second);

	  return out;
};



template <class Graph>
class Analyzer {
public :
	typedef Graph								graph_type;	
	typedef typename Graph::smart_ptr_type		smart_ptr_type;
	typedef typename Graph::ptr_type			ptr_type;
	typedef  Singleton<Analyzer >				Analyzer_Mgr;
	Analyzer() : first_call_(new graph_type){
		current_call_ = first_call_.get();
	};
	virtual ~Analyzer(){};
	ptr_type operator()(const std::string & name){
		if (name == current_call_->Cell_.name_)
			return current_call_;
		smart_ptr_type ptr = current_call_->Childs_[name];
		if (ptr == 0) {
			ptr = smart_ptr_type(new graph_type(current_call_));
			ptr->Cell_.name_ = name;
			current_call_->Childs_[name] = ptr;
		}
		current_call_= ptr.get();
		return current_call_;
	};
	void clear(ptr_type from = first_call_.get()){
		from->Childs_.clear();
	};
	void up() {
		current_call_ = current_call_->Parent_;
	};
	
	ptr_type		current_call_;
	smart_ptr_type	first_call_;
};

template <class P>
struct Common_Cell {
	typedef typename P cell_parameter_type;
	Common_Cell() : count_(0) {};
	Common_Cell(const std::string & name) : name_(name), count_(0) {};
	virtual ~Common_Cell(){};
	virtual cell_parameter_type operator()() const = 0 ;
	void operator()(const cell_parameter_type &) const {
	};
	std::string name_;
	OS_size count_;
};

template <class A, class C>
struct Analyzer_Common {
		typedef  Analyzer_Common								type;
		typedef  C												cell_type;
		typedef	typename Call_Graph<type>::ptr_type				ptr_type;
		typedef  Singleton<Analyzer<Call_Graph<type> > >		Analyzer_Mgr;
		typedef typename C::cell_parameter_type					cell_parameter_type;

		Analyzer_Common(const std::string & name) : ended_(false)
		{
			ptr_ = Analyzer_Mgr::instance()(name);
		};
		void clear() {
			Analyzer_Mgr::instance().clear(ptr_);
			end();
		};
		virtual void finish(const cell_parameter_type& p) {
			if (ended_==false)
				Analyzer_Mgr::instance().up();
			ended_ = true;
			(*ptr_)( p );
		};
		virtual void end() = 0;
		ptr_type ptr_;
		bool ended_;
};
template <class A, class C>
inline std::ostream &operator<<(std::ostream &out, const Analyzer_Common<A,C> &v){return out<< *v.ptr_ ;};



struct Analyzer_Time_Cell : public Common_Cell<OS_double> {
		typedef OS_double cell_parameter_type;
		typedef Common_Cell<OS_double> Base;
		Analyzer_Time_Cell() : time_(0) {};
		Analyzer_Time_Cell(const std::string & name) : time_(0), Base(name) {};
		virtual ~Analyzer_Time_Cell(){};
		virtual OS_double operator()() const {return time_;};
		void operator()(const cell_parameter_type& time) {
			time_+=time;
			count_++;
		};
		cell_parameter_type time_;
	};

std::ostream &operator<<(std::ostream &out, const Analyzer_Time_Cell &v);

class Analyzer_Timer : public Analyzer_Common<Analyzer_Timer,Analyzer_Time_Cell >{
	public :
		typedef Analyzer_Common<Analyzer_Timer,Analyzer_Time_Cell > Base;
		Analyzer_Timer(const std::string & name) : Base(name) {};
		virtual ~Analyzer_Timer(){ end(); };
		virtual void end() {
			finish(timer_.elapsed());
		};
		OS_timer timer_;
	};

#endif